package com.example.adminframework.util;


import com.example.adminframework.common.exception.CommonException;
import com.example.adminframework.constant.ErrorCode;
import com.example.adminframework.constant.HttpCode;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.ObjectUtils;

import java.io.*;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 类描述：bean复制工具类
 */
public class CopyUtil {

    private static Pattern humpPattern = Pattern.compile("[A-Z]");

    /**
     * 解决bean复制的null覆盖问题
     *
     * @param source source
     * @return {@link String[]}
     */
    public static String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    /**
     * 复制属性
     * 解决bean复制的null覆盖问题
     *
     * @param sources 源
     * @param target  目标
     */
    public static void copyProperties(Object sources, Object target) {
        if (ObjectUtils.isEmpty(sources) || ObjectUtils.isEmpty(target)) {
            return;
        }
        BeanUtils.copyProperties(sources, target, getNullPropertyNames(sources));
    }

    /**
     * 复制属性
     * 解决bean复制的null覆盖问题
     *
     * @param sources 源
     */
    public static <T> T copyPropertiesReturn(Object sources, Class<T> tClass) {
        if (ObjectUtils.isEmpty(sources)) {
            return null;
        }
        try {
            T t = tClass.newInstance();
            BeanUtils.copyProperties(sources, t, getNullPropertyNames(sources));
            return t;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Set转ArrayList
     *
     * @param set 集
     * @return {@link List<T>}
     */
    public static <T> List<T> set2List(Set<T> set) {
        return new ArrayList<T>(set);
    }

    /**
     * 复制list
     *
     * @param source   来源
     * @param purpose  用于
     * @param classObj 类obj
     */
    public static <T, S> void copyList(List<S> source, List<T> purpose, Class<T> classObj) {
        if ((!Objects.isNull(source)) && (!Objects.isNull(purpose))) {
            source.forEach(item -> {
                try {
                    T data = classObj.newInstance();
                    BeanUtils.copyProperties(item, data);
                    purpose.add(data);
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new CommonException(HttpCode.INTERNAL_SERVER_ERROR, ErrorCode.UN_KNOWN_ERROR);
                }
            });
        }
    }

    /**
     * 复制list
     *
     * @param source   来源
     * @param classObj 类obj
     */
    public static <T, S> List<T> copyListReturn(List<S> source, Class<T> classObj) {
        List<T> listObj = new ArrayList<>();
        if ((!Objects.isNull(source))) {
            source.forEach(item -> {
                try {
                    T data = classObj.newInstance();
                    BeanUtils.copyProperties(item, data);
                    listObj.add(data);
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new CommonException(HttpCode.INTERNAL_SERVER_ERROR, ErrorCode.UN_KNOWN_ERROR);
                }
            });
        }
        return listObj;
    }


    /**
     * 对于可序列化(实现Serializable接口)的对象集合进行深拷贝
     * @param src
     * @param <T>
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }

}
